import { defineComponent, ref, computed, reactive, watch, watchEffect, provide, onMounted, createVNode, createTextVNode, unref } from 'vue';
import EmptyOption from './EmptyOption.js';
import { Value } from '../../inner/Value.js';
import Dropdown from '../../Dropdown/index.js';
import InnerOption from './InnerOption.js';
import VirtualList from '../../virtual-list/index.js';
import formFieldRef from '../../use/formFieldRef.js';
import { FeatherChevronDown } from 'cui-vue-icons/feather';

const Select = /* @__PURE__ */ defineComponent({
  name: 'Select',
  props: {
    name: {
      type: String
    },
    modelValue: {
      type: [String, Number, Array],
      default: undefined
    },
    disabled: {
      type: Boolean
    },
    size: {
      type: String
    },
    clearable: {
      type: Boolean
    },
    multi: {
      type: Boolean
    },
    prefix: {
      type: [String, Number, Object]
    },
    placeholder: {
      type: String
    },
    data: {
      type: Array
    },
    textField: {
      type: String,
      default: 'label'
    },
    valueField: {
      type: String,
      default: 'value'
    },
    filter: {
      type: Boolean
    },
    renderOption: {
      type: Function
    },
    renderSelectedItem: {
      type: Function
    },
    emptyOption: {
      type: [Object, String]
    },
    showMax: {
      type: Number
    },
    max: {
      type: Number
    },
    status: {
      type: String
    },
    footer: {
      type: [String, Object]
    },
    header: {
      type: [String, Object]
    },
    triggerRender: {
      type: Function
    },
    valueClosable: {
      type: Boolean
    },
    transfer: {
      type: Boolean
    },
    align: {
      type: String,
      default: 'bottomLeft'
    },
    showMore: {
      type: Boolean
    },
    maxHeight: {
      type: Number
    },
    loading: {
      type: Boolean
    },
    remoteMethod: {
      type: Function
    },
    debounceTime: {
      type: Number
    },
    asFormField: {
      type: Boolean,
      default: true
    },
    defaultLabel: {
      type: [String, Array]
    }
  },
  emits: ['change', 'update:modelValue', 'exceed'],
  setup(props, {
    emit,
    slots
  }) {
    const open = ref(false);
    const align = props.align ?? 'bottomLeft';
    const value = formFieldRef(props, emit, props.modelValue ?? props.multi ? [] : '');
    const allTextNodes = [];
    const classList = computed(() => ({
      'cm-select': true,
      'cm-select-disabled': props.disabled,
      [`cm-select-${props.size}`]: props.size,
      'cm-select-clearable': !props.disabled && props.clearable && `${value.value}`.length !== 0,
      'cm-select-multi': props.multi,
      'cm-select-open': open.value,
      'cm-select-with-prefix': props.prefix,
      [`cm-select-status-${props.status}`]: props.status
    }));
    const filterWrap = ref();
    const textField = props.textField || 'label';
    const valueField = props.valueField || 'value';
    let initLabels = [];
    if (props.filter && props.defaultLabel) {
      if (props.multi && props.defaultLabel instanceof Array) {
        props.defaultLabel.forEach((label, index) => {
          initLabels.push({
            [valueField]: value.value[index],
            [textField]: label,
            _checked: true,
            _show: true
          });
        });
      } else {
        initLabels = [{
          [valueField]: value.value,
          [textField]: props.defaultLabel,
          _checked: true,
          _show: true
        }];
      }
    }
    let isClickChanging = true;
    const query = ref('');
    // 当单选且有默认label时先禁止查询，后放开
    queueMicrotask(() => {
      isClickChanging = false;
    });
    let dataMap = {};
    const emptyOption = {
      [valueField]: '',
      [textField]: props.emptyOption,
      _show: true,
      emptyOption: true
    };
    if (props.emptyOption) {
      dataMap[''] = emptyOption;
    }
    const showLabels = ref(initLabels);
    let debounceTimer = null;

    // 重新构建数据
    function buildData(arr, target) {
      arr && arr.forEach(item => {
        target.push(item);
        item._show = true;
        dataMap[item[valueField]] = item;
        if (item.items) {
          buildData(item.items, target);
        }
      });
    }

    // 传入的data变化同步更新
    const newData = computed(() => {
      const data = props.data || [];
      dataMap = {};
      const newData = [];
      if (props.emptyOption) {
        newData.push({
          [valueField]: '',
          [textField]: props.emptyOption,
          _show: true,
          emptyOption: true
        });
      }
      if (initLabels) {
        initLabels.forEach(label => {
          newData.push({
            ...label,
            _show: true
          });
        });
      }
      if (data) {
        buildData(data, newData);
      }
      return newData;
    });
    const store = reactive({
      list: props.emptyOption ? [emptyOption].concat([...initLabels]) : [].concat([...initLabels])
    });
    watch(() => props.data, () => {
      const val = value.value;
      store.list = newData.value;
      const labels = [];
      store.list.forEach(item => {
        if (props.multi) {
          item._checked = val.includes(item[valueField]);
        } else {
          item._checked = val === item[valueField];
        }
        if (item._checked) {
          labels.push(item);
        }
      });
      showLabels.value = labels;
    }, {
      immediate: true
    });

    // 点击更新setValue 并触发onChange事件
    const onOptionClick = (v, option) => {
      if (dataMap[v]) {
        if (dataMap[v].group) {
          return;
        }
      }
      let arr = unref(showLabels);
      if (props.multi) {
        let val = value.value;
        const index = val.indexOf(v);
        if (index > -1) {
          val.splice(index, 1);
          arr.splice(index, 1);
        } else {
          if (props.max && val.length >= props.max) {
            emit('exceed');
            return;
          }
          val = [...val];
          val.push(v);
          arr.push(option);
        }
        value.value = [...val];
        query.value = '';
        emit('change', val);
      } else {
        isClickChanging = true;
        arr = [option];
        value.value = v;
        Promise.resolve().then(() => {
          isClickChanging = false;
        });
        query.value = '';
        open.value = false;
        emit('change', v, option);
      }
    };

    // const onVisibleChange = (visible) => {
    //     if (!visible) {
    //         query.value = '';
    //     }
    // };

    watchEffect(() => {
      const arr = [];
      store.list.forEach(item => {
        if (item._checked) {
          arr.push({
            id: item[valueField],
            title: item[textField]
          });
        }
      });
      let labels = [];
      if (props.multi) {
        labels = arr.length ? arr : props.emptyOption ? [{
          id: '',
          title: props.emptyOption
        }] : [];
      } else {
        labels = arr.length ? arr[0].title : props.emptyOption ? props.emptyOption : '';
      }
      showLabels.value = labels;
    });

    // 清空数据
    const onClear = e => {
      showLabels.value = [];
      if (props.multi) {
        props.onChange && props.onChange([]);
        value.value = [];
      } else {
        props.onChange && props.onChange('');
        value.value = '';
        query.value = '';
        open.value = false;
      }
    };

    // 过滤查询
    watchEffect(() => {
      const queryStr = query.value;
      // 远程查询
      if (props.remoteMethod) {
        if (isClickChanging) {
          return;
        }
        if (queryStr) {
          initLabels = [];
          clearTimeout(debounceTimer);
          debounceTimer = setTimeout(() => {
            props.remoteMethod?.(queryStr);
            // 保持打开状态
            open.value = true;
          }, props.debounceTime || 300);
        }
      } else {
        // 本地过滤
        store.list.forEach(item => {
          item._show = item[textField].indexOf(queryStr) > -1;
        });
        // 高亮搜索字符
        queueMicrotask(() => {
          buildNodes();
          hilightKeyword(queryStr);
        });
      }
    });

    /**
    * 构建搜索的节点
    * @returns
    */
    const buildNodes = () => {
      // 不支持高亮则返回
      if (!CSS.highlights) {
        return;
      }
      const treeWalker = document.createTreeWalker(filterWrap.value, NodeFilter.SHOW_TEXT);
      let currentNode = treeWalker.nextNode();
      while (currentNode) {
        allTextNodes.push(currentNode);
        currentNode = treeWalker.nextNode();
      }
    };

    // 高亮关键字
    const hilightKeyword = queryStr => {
      // 不支持高亮则返回
      if (!CSS.highlights) {
        return;
      }
      CSS.highlights.delete('cm-search-results');
      const str = queryStr.trim().toLowerCase();
      if (!str) {
        return;
      }
      const ranges = allTextNodes.map(el => {
        return {
          el,
          text: el.textContent?.toLowerCase()
        };
      }).map(({
        text,
        el
      }) => {
        const indices = [];
        let startPos = 0;
        while (text && startPos < text.length) {
          const index = text.indexOf(str, startPos);
          if (index === -1) break;
          indices.push(index);
          startPos = index + str.length;
        }
        return indices.map(index => {
          const range = new Range();
          range.setStart(el, index);
          range.setEnd(el, index + str.length);
          return range;
        });
      });
      const searchResultsHighlight = new window.Highlight(...ranges.flat());
      CSS.highlights.set('cm-search-results', searchResultsHighlight);
    };

    // 多选场景下删除value
    const onValueClose = (item, e) => {
      if (props.multi) {
        const arr = showLabels.value;
        const val = value.value;
        const index = val.indexOf(item.id);
        if (index > -1) {
          val.splice(index, 1);
          arr.splice(index, 1);
        }
        value.value = [...val];
        showLabels.value = [...arr];
        emit('change', val);
      }
    };

    // 撤消按键，删除最后一个value
    const onDeleteLastValue = () => {
      if (props.multi) {
        const arr = showLabels.value;
        const val = value.value;
        if (val.length > 0) {
          val.pop();
          arr.pop();
          value.value = [...val];
          showLabels.value = [...arr];
          emit('change', val);
        }
      }
    };
    const displayItems = computed(() => {
      return store.list.filter(item => item._show);
    });
    provide('CMSelectContext', {
      addOption: option => {
        option._show = true;
        dataMap[option[valueField]] = option;
        store.list.push(option);
      },
      removeOption: option => {
        delete dataMap[option[valueField]];
        store.list = store.list.filter(item => item[valueField] !== option[valueField]);
      }
    });
    onMounted(() => {
      // 将选中的数据同步至store的数据项中
      watchEffect(() => {
        const val = value.value;
        store.list.forEach(item => {
          if (props.multi) {
            item._checked = val.includes(item[valueField]);
          } else {
            item._checked = val === item[valueField];
          }
        });
      });
    });
    return () => createVNode("div", {
      "class": classList.value
    }, [slots.default?.(), createVNode(Dropdown, {
      "transfer": props.transfer,
      "align": align,
      "disabled": props.disabled,
      "trigger": "click",
      "modelValue": open.value,
      "onUpdate:modelValue": $event => open.value = $event,
      "menu": createVNode("div", {
        "class": "cm-select-options-wrap"
      }, [props.header ? createVNode("div", {
        "class": "cm-select-header"
      }, [props.header]) : null, createVNode("div", {
        "class": "cm-select-options",
        "style": {
          'max-height': props.maxHeight ? `${props.maxHeight}px` : ''
        }
      }, [props.loading ? createVNode("div", {
        "class": "cm-select-loading"
      }, [createTextVNode("\u52A0\u8F7D\u4E2D")]) : createVNode("ul", {
        "class": "cm-select-option-list",
        "ref": filterWrap
      }, [createVNode(VirtualList, {
        "items": displayItems.value,
        "itemEstimatedSize": 30,
        "maxHeight": 200
      }, {
        default: scope => {
          return createVNode(VirOption, {
            "textField": textField,
            "valueField": valueField,
            "value": value.value,
            "onClear": onClear,
            "onOptionClick": onOptionClick,
            "renderOption": props.renderOption,
            "item": scope.item,
            "index": scope.index
          }, null);
        }
      })])]), props.footer ? createVNode("div", {
        "class": "cm-select-footer"
      }, [props.footer]) : null])
    }, {
      default: () => [props.triggerRender ? createVNode("span", {
        "class": "cm-select-trigger"
      }, [props.triggerRender?.(showLabels.value)]) : createVNode(Value, {
        "text": showLabels.value,
        "multi": props.multi,
        "showMax": props.showMax,
        "disabled": props.disabled,
        "showMore": props.showMore,
        "valueClosable": props.valueClosable || props.filter,
        "clearable": props.clearable,
        "onClear": onClear,
        "placeholder": props.placeholder,
        "prepend": props.prefix,
        "size": props.size,
        "icon": createVNode(FeatherChevronDown, {
          "class": "cm-select-cert"
        }, null),
        "onClose": onValueClose,
        "query": query,
        "filter": props.filter,
        "onDeleteLastValue": onDeleteLastValue
      }, null)]
    })]);
  }
});
function VirOption(props) {
  const item = props.item;
  if (item.emptyOption) {
    return createVNode(EmptyOption, {
      "data": {
        label: item[props.textField],
        value: ''
      },
      "checked": props.value === '',
      "onClick": props.onClear
    }, null);
  } else {
    return createVNode(InnerOption, {
      "key": item[props.valueField],
      "renderOption": props.renderOption,
      "visible": item._show,
      "disabled": item.disabled,
      "data": item,
      "checked": item._checked,
      "textField": props.textField,
      "valueField": props.valueField,
      "onClick": props.onOptionClick
    }, null);
  }
}
VirOption.displayName = 'VirOption';

export { Select as default };
